为什么你的 Spring Task 定时任务没有定时执行?
The following article is from 侠梦的开发笔记 Author 侠梦
(给ImportNew加星标,提高Java技能)
来自作者投稿 作者:侠梦
Spring Task 的 crontab 的表达式 和linux下的 crontab 有什么区别? crontab 表达式记不住? 定时任务阻塞会有什么影响? 多个定时任务的情况下如何运行的? 具有相同表达式的定时任务,他们的执行顺序如何? 为什么async异步任务没有生效?
在使用时,需要在Application 启动类上加上 @EnableScheduling 注解,它是从Spring 3.1后开始提供的。
由于Spring3 版本较低,使用得比较少了,我们使用高版本可能并不会考虑太多细节,大多只需要关注目标实现,所以我们在配套使用两个注解的时候,并不会出现什么问题。
在3.0 中 ,是通过
<!-- 配置任务线性池 -->
<!-- 任务执行器线程数量 -->
<task:executor id="executor" pool-size="3" />
<!-- 任务调度器线程数量 -->
<task:scheduler id="scheduler" pool-size="3" />
<!-- 启用annotation方式 -->
<task:annotation-driven scheduler="scheduler"
executor="executor" proxy-target-class="true" />
任务一直阻塞会怎么样?
介绍了两个注解的作用后,我们来开始做实验,简单的写一个定时执行的方法。
Executors.newSingleThreadScheduledExecutor();
串行当然很好理解,就是上文说的汽车过桥,依次通过。再来理解并发,区别于并行,并发是指一个处理器同时处理多个任务,而并行是指多个(核)处理器同时处理多个不同的任务。并发不一定同一时间发生,而并行,指的是同一时间。
从上面的实验同样能知道,具有相同表达式的定时任务,还是和调度有关,如果是默认的线程池,那么会串行执行,首先获取到 cpu 时间片的先执行。在多线程情况下,具体的先后执行顺序和线程池线程数和所用线程池所用队列等等因素有关。
Spring Task和linux crontab的cron语法区别?
两者的 cron 表达式其实很相似,需要注意的是 linux 的 crontab 只为我们提供了最小颗粒度为分钟级的任务,而 java 中最小的粒度是从秒开始的。具体细节如下图:
在cron语法中容易犯的错误
并发执行任务如何配置?
可实现 AsyncConfigurer 接口复写 getAsyncExecutor 获取异步执行器,getAsyncUncaughtExceptionHandler 获取异步未捕获异常处理器
@Configurationpublic
class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
@Scheduled(fixedRate = 1000*10,initialDelay = 1000*20)
@Async("hyqThreadPoolTaskExecutor")
public void test(){
System.out.println(Thread.currentThread().getName()+"--->xxxxx--->"+Thread.currentThread().getId());
}
//自定义线程池
@Bean(name = "hyqThreadPoolTaskExecutor")
public TaskExecutor getMyThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(20);
taskExecutor.setMaxPoolSize(200);
taskExecutor.setQueueCapacity(25);
taskExecutor.setKeepAliveSeconds(200);
taskExecutor.setThreadNamePrefix("hyq-threadPool-");
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
taskExecutor.initialize();
return taskExecutor;
}
其他问题
如果是定时任务没有生效,需要检查 @EnableScheduling 注解是否加上。如果是异步没有生效,需要检查 @EnableAsync 注解是否加上,并且定义线程池,否则仍然是串行执行的。
总结
文章介绍了SpringBoot 定时任务的原理, 3.0 版本前后的区别,通过单线程任务阻塞实验,探究了延迟队列及串行、并行、并发的概念。对比了linux下的 crontab 和spring的cron 表达式区别以及常犯的错误。最后通过实验异步注解,两种方式配置线程池,让任务高效运作。
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
好文章,我在看❤️